<template>
  <el-select
    ref="remoteSelect"
    :value="value"
    v-bind="$attrs"
    :multiple="multiple"
    remote
    filterable
    :remote-method="remoteMethod"
    clearable
    @change="changeHandle"
    @focus="focusHandle"
    @input="inputHandle"
    v-on="listeners"
  >
    <el-option v-for="o in options" :key="o.id" :value="o[valueName]" :label="o[labelName]" />
  </el-select>
</template>
<script>
import request from '@/utils/request'
import { otherFiltersItem } from '@/utils'
export default {
  name: 'CRemoteSelect',
  props: {
    value: {
      type: [String, Number, Array],
      default: ''
    },
    label: {
      type: [String, Number, Array],
      default: ''
    },
    labelName: {
      type: String,
      default: 'label'
    },
    valueName: {
      type: String,
      default: 'value'
    },
    method: {
      type: String,
      default: 'POST'
    },
    url: {
      type: String,
      default: ''
    },
    filtersRaw: {
      type: Array,
      default: () => []
    },
    paramsRaw: {
      type: Object,
      default: () => ({})
    },
    remoteFun: {
      type: Function,
      default: undefined
    },
    obj: {
      type: Object,
      default: () => ({})
    },
    embedParams: {
      type: Boolean,
      default: false
    },
    handlerFun: {
      type: Function,
      default: res => {
        if (res.data) {
          if (res.data.records) {
            return res.data.records
          } else {
            return res.data
          }
        } else {
          return res || []
        }
      }
    },
    pageFlag: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      options: [],
      listeners: {}
    }
  },
  computed: {
    multiple() {
      return this.value instanceof Array
    }
  },
  watch: {
    value: {
      handler(v) {
        if (v === '' || v === undefined) return
        this.remoteMethod(this.label)
      },
      immediate: true
    }
  },
  created() {
    this.listeners = { ...this.$listeners }
    delete this.listeners.input
    delete this.listeners.change
    delete this.listeners.focus
  },
  methods: {
    remoteMethod(v) {
      let result = Promise.resolve([])
      if (this.remoteFun instanceof Function) {
        let res = this.remoteFun(v)
        res instanceof Promise ? '' : res = Promise.resolve(res)
        result = res.then(this.handlerFun)
      } else if (this.method.toUpperCase() === 'POST') {
        result = this.postRemote(v).then(this.handlerFun)
      } else if (this.method.toUpperCase() === 'GET') {
        result = this.getRemote(v).then(this.handlerFun)
      }
      result.then(res => {
        this.options = res
      })
    },
    setLabel(res) {
      if (this.multiple) {
        const t = []
        for (const v of this.value) {
          for (const l of this.options) {
            if (l[this.valueName] === v) {
              t.push({
                currentLabel: l[this.labelName],
                hitState: false,
                value: this.valueName
              })
            }
          }
        }
        t.length > 0 && this.$refs.remoteSelect ? this.$refs.remoteSelect.selected = t : ''
      }
    },
    getUrl(v) {
      const query = []
      if (v !== undefined || v !== '') {
        query.push(`${this.labelName}=${v}`)
      }
      for (const [k, v] of Object.entries(this.paramsRaw)) {
        if (v instanceof Array) {
          v.forEach(e => {
            query.push(`${k}=${e}`)
          })
        } else {
          query.push(`${k}=${v}`)
        }
      }
      let url = this.url
      if (/\?(\w+=\w+)+/.test(url)) {
        url = `${url}&${query.join('&')}`
      } else {
        url = `${url}?${query.join('&')}`
      }
      return url
    },
    postRemote(v) {
      const data = new FormData()
      // otherFiltersItem
      const filtersRaw = []
      for (const v of this.filtersRaw) {
        if (Object.prototype.toString.call(v) === '[object Object]') {
          // eslint-disable-next-line prefer-const
          let { key, value, operator } = v
          if (value instanceof Array) {
            value = value.join(',')
            operator = 'in'
          }
          filtersRaw.push(otherFiltersItem(key, value, operator || 'like'))
        }
      }
      const paramsRaw = { ...this.paramsRaw }
      if (v !== undefined && v !== '') {
        if (!this.embedParams) {
          filtersRaw.push(otherFiltersItem(this.labelName, v, 'like'))
        } else {
          paramsRaw[this.labelName] = v
        }
      }
      data.append('filtersRaw', JSON.stringify(filtersRaw))
      if (Object.keys(paramsRaw).length > 0) {
        data.append('paramsRaw', JSON.stringify(paramsRaw))
      }
      data.append('pageFlag', this.pageFlag)
      return request({
        url: this.url,
        method: 'POST',
        data
      })
    },
    getRemote(v) {
      return request({
        url: this.getUrl(v),
        method: 'GET'
      })
    },
    inputHandle(v) {
      this.$emit('input', v)
    },
    changeHandle(v) {
      this.$emit('change', v)
      const labels = []
      const objs = []
      if (!this.multiple) v = [v]
      for (const item of v) {
        for (const opt of this.options) {
          if (item === opt[this.valueName]) {
            labels.push(opt[this.labelName])
            objs.push(opt)
            continue
          }
        }
      }
      if (this.multiple) {
        this.$emit('update:label', labels)
        this.$emit('update:obj', objs)
      } else {
        this.$emit('update:label', labels[0])
        this.$emit('update:obj', objs[0])
      }
    },
    focusHandle() {
      this.$emit('focus')
      this.remoteMethod(this.label)
    }
  }
}
</script>
